package com.ES02Hotel.service.impl;

import com.ES02Hotel.HotelDemoApplication;
import com.ES02Hotel.mapper.HotelMapper;
import com.ES02Hotel.pojo.Hotel;
import com.ES02Hotel.pojo.HotelDoc;
import com.ES02Hotel.pojo.PageResult;
import com.ES02Hotel.pojo.RequestParams;
import com.ES02Hotel.service.IHotelService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.web.bind.annotation.RequestParam;

import javax.naming.directory.SearchResult;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Override
    public PageResult search(RequestParams params) {
        try {
            //创建搜索请求
            SearchRequest request=new SearchRequest("hotel");

            //2.设置条件
           buildBasic(params,request);

            //4.执行请求
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            //5.解析
            return  handleResponse(response);

        } catch (IOException e) {
            e.printStackTrace();
            log.error("异常："+e.getMessage());
        }

        return null;
    }


    /**
     * 搜索条件
     * @param params
     * @param request
     */
    void buildBasic(RequestParams params,SearchRequest request){

        //1准备bool
        BoolQueryBuilder builder =QueryBuilders.boolQuery();

        // 1.1.关键字搜索，match查询，放到must中
        String key = params.getKey();
        if (StringUtils.isNotBlank(key)){
            // 不为空，根据关键字查询
           builder.must(QueryBuilders.matchQuery("all",key));
        }else {
            // 为空，查询所有
            builder.must(QueryBuilders.matchAllQuery());
        }
        // 1.2.品牌
        String brand = params.getBrand();
        if (StringUtils.isNotBlank(brand)) {
            builder.filter(QueryBuilders.termQuery("brand", brand));
        }
        // 1.3.城市
        String city = params.getCity();
        if (StringUtils.isNotBlank(city)) {
            builder.filter(QueryBuilders.termQuery("city", city));
        }
        // 1.4.星级
        String starName = params.getStarName();
        if (StringUtils.isNotBlank(starName)) {
            builder.filter(QueryBuilders.termQuery("starName", starName));
        }
        // 1.5.价格
        if (params.getMinPrice() != null && params.getMaxPrice() != null) {
            builder.filter(QueryBuilders.rangeQuery("price").gte(params.getMinPrice()).lte(params.getMaxPrice()));
        }

        //2.分页
        Integer page=params.getPage();
        Integer size=params.getSize();
        request.source().from((page-1)*size).size(size);

        //3.距离
        if (StringUtils.isNotBlank(params.getLocation())){
            request.source()
                    .sort(SortBuilders.geoDistanceSort("location",new GeoPoint(params.getLocation()))
                            .order(SortOrder.ASC)
                            .unit(DistanceUnit.KILOMETERS) //距离单位
                    );
        }

        // 4.得加钱
        FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery(
                builder, // 原始查询，boolQuery
                new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{ // function数组
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                QueryBuilders.termQuery("isAD", true), // 过滤条件
                                ScoreFunctionBuilders.weightFactorFunction(10) // 算分函数
                        )
                }
        );

        // 5.设置查询条件
        request.source().query(functionScoreQuery);
    }


    /**
     * 处理结果
     * @param response
     * @return
     */
    PageResult handleResponse(SearchResponse response){
        SearchHits hits=response.getHits();
        long totalHits = hits.getTotalHits().value;//总数量

        SearchHit[] hits1 = hits.getHits();
        List<HotelDoc>  list=new ArrayList<>();
        for (SearchHit hit : hits1) {
            HotelDoc hotelDoc = JSON.parseObject(hit.getSourceAsString(), HotelDoc.class);

            //查询距离
            Object[] sortValues = hit.getSortValues();
            if (sortValues.length>0){
                //获取排序之后的值，设置到距离字段中
                hotelDoc.setDistance(sortValues[0]);
            }

            list.add(hotelDoc);
        }
        return new PageResult(totalHits,list);
    }
}
